Skip to content

S16-00 专题-JS-Storage

[TOC]

索引

基本属性

  • Storage.lengthnumber,只读,表示存储中键值对的总数。

基本方法

概述

Storage

Storage:是Web开发中用于在客户端(浏览器)持久化存储数据的技术,允许网页在不依赖服务器的情况下保存用户信息。

Storage主要提供了一种机制,可以让浏览器提供一种比cookie更直观的key、value存储方式。

Storage 分类

  • localStorage:是 HTML5 Web Storage API 的一部分,用于在客户端(浏览器)持久化存储键值对数据。
  • sessionStorage:是 HTML5 Web Storage API 的一部分,用于在客户端(浏览器)临时存储键值对数据。
  • Cookies:是网站存储在用户浏览器中的小型文本数据(通常小于 4KB),用于在客户端和服务端之间传递信息。
  • IndexedDB

图例

image-20230620151632534

image-20230620151646589

对比localStorage、sessionStorage

我们会发现localStorage和sessionStorage看起来非常的相似。

那么它们有什么区别呢?

  • 验证一:关闭网页后重新打开,localStorage会保留,而sessionStorage会被删除;

  • 验证二:在页面内实现跳转,localStorage会保留,sessionStorage也会保留;

  • 验证三:在页面外实现跳转(打开新的网页),localStorage会保留,sessionStorage不会被保留;

localStorage

概述

localStorage:是 HTML5 Web Storage API 的一部分,用于在客户端(浏览器)持久化存储键值对数据。

核心特点

  • 持久性:数据除非手动删除(用户清除浏览器缓存或开发者调用API),否则永久保留。
  • 同源策略:同一域名、协议、端口下的页面共享同一存储空间,不同源页面无法互相访问。
  • 容量限制:通常为 5MB(不同浏览器可能略有差异),超出时会抛出错误。
  • 仅支持字符串:键和值均为字符串,存储对象需序列化(如 JSON.stringify())。

API-localStorage

属性

  • storage.lengthnumber,只读,表示存储中键值对的总数。

方法

sessionStorage

概述

sessionStorage:是 HTML5 Web Storage API 的一部分,用于在客户端(浏览器)临时存储键值对数据。

核心特点

  • 会话级存储:数据仅在当前浏览器标签页或窗口有效,关闭后自动清除。
  • 同源策略:同一域名、协议、端口下的页面共享同一存储空间,但不同标签页的 sessionStorage 相互隔离
  • 容量限制:通常为 5MB(与 localStorage 一致),超出时会抛出错误。
  • 仅支持字符串:键和值均为字符串,存储对象需序列化(如 JSON.stringify())。

API-sessionStorage

localStorage 一致

Cookies

概述

Cookies:是网站存储在用户浏览器中的小型文本数据(通常小于 4KB),用于在客户端和服务端之间传递信息。

核心特点

  • 生命周期:可设置过期时间(ExpiresMax-Age),否则随浏览器关闭失效(会话Cookie)。
  • 作用域:通过 DomainPath 控制可访问的域名和路径。
  • 存储容量:每个 Cookie 最大 4KB,每个域名通常最多允许 50-150 个 Cookies。
  • 自动携带:每次 HTTP 请求会自动附加同域名的 Cookies 到请求头的 Cookie 字段。

主要用途

  • 会话管理:保持用户登录状态、记录购物车内容等。
  • 个性化:保存用户偏好(如语言、主题)。
  • 行为跟踪:记录用户行为用于分析或广告定向(需符合隐私法规)。

语法格式

text
name=value; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Max-Age=3600; Domain=example.com; Path=/; Secure; HttpOnly; SameSite=Lax
  • 必需字段
    • name=value:键值对,内容需 URL 编码(如空格转为 %20)。
  • 可选属性
    • Expires:过期时间(GMT 格式)。
    • Max-Age:存活时间(秒,优先级高于 Expires)。
    • Domain:指定可访问的域名(默认为当前域名,不包含子域名)。
    • Path:指定可访问的路径(默认为当前路径)。
    • Secure:仅通过 HTTPS 传输。
    • HttpOnly:禁止 JavaScript 访问,防 XSS 攻击。
    • SameSite:限制跨站请求携带 Cookie(值:Strict/Lax/None)。

操作方式

不同环境有不同的操作 Cookies 的方法:

服务端操作

服务端设置:通过 Set-Cookie HTTP 响应头创建或更新 Cookie:

http
HTTP/1.1 200 OK
Set-Cookie: user_id=123; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Secure; HttpOnly

示例:服务端(Node.js)设置 Cookie

js
const http = require('http');

http.createServer((req, res) => {
  res.setHeader('Set-Cookie', [
    'user_id=123; HttpOnly; Max-Age=3600',
    'theme=dark; SameSite=Lax'
  ]);
  res.end('Cookies set!');
}).listen(3000);

客户端操作

客户端设置:通过 JS 中的 document.cookie 设置/获取 Cookies:

js
// 创建 Cookie(需手动编码)
document.cookie = "username=John%20Doe; expires=Thu, 18 Dec 2024 12:00:00 UTC; path=/; Secure";

// 读取所有 Cookies
const cookies = document.cookie; // 返回字符串 "cookie1=value1; cookie2=value2"

// 删除 Cookie(设置过期时间为过去)
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";

示例:客户端读取 Cookie

js
// 解析 Cookies 为对象
const cookies = document.cookie.split('; ').reduce((acc, cookie) => {
  const [name, value] = cookie.split('=');
  acc[name] = decodeURIComponent(value);
  return acc;
}, {});

console.log(cookies.user_id); // 输出 "123"

安全问题

CSRF(Cross-site Request Forgery,跨站请求伪造):攻击者诱导用户访问恶意页面,利用已登录的身份发起非法请求(如转账)。

防御

  1. 设置 SameSite=Lax(默认值,阻止跨站 POST 请求携带 Cookie)。
  2. 添加 CSRF Token 到请求中验证来源。

XSS(Cross-site scripting,跨站脚本攻击):恶意脚本窃取 Cookies。

防御

  1. 敏感 Cookie 设置 HttpOnly,禁止 JavaScript 访问。
  2. 对用户输入严格过滤和转义。

最佳安全实践

  1. 敏感信息加密

    js
    // 服务端加密 Cookie 值
    const encryptedValue = encryptAES('user123', secretKey);
    response.setHeader('Set-Cookie', `session=${encryptedValue}; HttpOnly; Secure`);
  2. 最小化 Cookie 使用

    • 避免存储敏感数据(如密码、身份证号)。
    • 优先使用服务端 Session 管理身份验证。
  3. 隐私合规:遵循 GDPR、CCPA 等法规,明确告知用户并获取同意。

IndexedDB【

概述

IndexedDB:是浏览器提供的一种底层 异步 NoSQL 数据库,支持存储大量结构化数据(包括文件、Blob等),适用于复杂 Web 应用的客户端数据管理。

核心特点

  • 异步操作:所有读写操作非阻塞,通过事件或 Promise 处理结果。
  • 事务支持:确保数据操作的原子性和一致性。
  • 索引查询:支持基于键、索引的高效查询,类似关系型数据库。
  • 大容量存储:浏览器动态分配存储空间,通常可达数百MB甚至更高。
  • 同源策略:数据库按域名隔离,不同源页面无法互相访问。

进阶用法

封装Storage

基础封装

image-20230907173656557

image-20230907173654255

优化:存储对象类型

原生方法

image-20230907174001581

image-20230907174218915

image-20230907174248358

优化:兼容local和session

image-20230907174517196

image-20230907174540612

TS封装Storage@

使用TS封装的Cache类

ts
enum EStorage {
  Local,
  Session
}

/** 封装Storage */
class Cache {
  storage: Storage
  constructor(type: EStorage) {
    this.storage = type === EStorage.Local ? localStorage : sessionStorage
  }
  getItem(key: string) {
    const res = this.storage.getItem(key)
    return res ? JSON.parse(res) : ''
  }

  setItem(key: string, value: any) {
    this.storage.setItem(key, JSON.stringify(value || ''))
  }

  removeItem(key: string) {
    this.storage.removeItem(key)
  }

  clear() {
    this.storage.clear()
  }

  key(index: number) {
    return this.storage.key(index)
  }

  addItem(key: string, value: any) {
    let res = this.getItem(key)
    if (!res) {
      // 不存在key
      this.storage.setItem(key, JSON.stringify(value || ''))
    } else {
      // 存在key
      if (Array.isArray(res)) {
        res = Array.isArray(value) ? [...res, ...value] : [...res, value]
      } else if (isObjectLiteral(res)) {
        res = { ...res, ...value }
      } else {
        res += value + ''
      }
      this.storage.setItem(key, JSON.stringify(res))
    }
  }
}

export const localCache = new Cache(EStorage.Local)
export const sessionCache = new Cache(EStorage.Session)

工具方法

ts

/**
 *  判断是否为对象字面量
 * @param value 要判断的值
 * @returns {boolean} 是否为对象字面量
 */
function isObjectLiteral(value: any) {
  return Object.prototype.toString.call(value) === '[object Object]' && value !== null && !Array.isArray(value)
}

测试

ts

// 测试
localCache.setItem('age', 27)
// localCache.setItem('name', 'zhangsan')
// localCache.setItem('user', { name: 'zhangsan', age: 18 })
// localCache.setItem('count', ['one', 'two', 'three', 'four'])
// console.log(localCache.getItem('age'))
// console.log(localCache.getItem('name'))
// console.log(localCache.getItem('user'))
// console.log(localCache.getItem('count'))

// 添加值
// localCache.addItem('info', {})
// localCache.addItem('user', { city: 'beijing' })
// localCache.addItem('count', ['eight'])
// localCache.addItem('count', 'seven')
localCache.addItem('age', 10) // '2710'